﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CueMounter.DokanStructure.RamDisk {
	public class DokanRamDiskFile : DokanFile {
		private const int BlockSize = 2097152;

		private List<byte[]> Blocks;
		private long FileLength;

		public DokanRamDiskFile(string name) {
			Name = name;
			Blocks = new List<byte[]>();
			FileLength = 0;
		}

		private static long CalculateNeededBlocks(long targetLength) {
			long blocks = targetLength / BlockSize;
			if(blocks * BlockSize < targetLength) {
				return blocks + 1;
			}
			return blocks;
		}

		public override long Length {
			get {
				return FileLength;
			}
			set {
				int blocks = (int)CalculateNeededBlocks(value);
				if(blocks < Blocks.Count) {
					Blocks.RemoveRange(blocks, Blocks.Count - blocks);
				} else if(blocks > Blocks.Count) {
					Blocks.AddRange(Enumerable.Range(0, blocks - Blocks.Count).Select(i => new byte[BlockSize]));
				}
				FileLength = value;
			}
		}

		public override bool CanRead { get { return true; } }
		public override bool CanWrite { get { return true; } }

		public override long Read(long fileOffset, byte[] buffer, long bufferOffset, long length) {
			long bytesCopied = 0;
			while(bytesCopied < length) {
				int currentBlock = (int)(fileOffset / BlockSize);
				int currentBlockOffset = (int)(fileOffset - currentBlock * BlockSize);
				long bytesToCopy = Math.Min(BlockSize - currentBlockOffset, Math.Min(length, FileLength - fileOffset));
				if(bytesToCopy == 0) {
					break;
				}

				Array.Copy(Blocks[currentBlock], currentBlockOffset, buffer, bufferOffset, bytesToCopy);

				bytesCopied += bytesToCopy;
				fileOffset += bytesToCopy;
				bufferOffset += bytesToCopy;
				length -= bytesToCopy;
			}
			return bytesCopied;
		}

		public override long Write(long fileOffset, byte[] buffer, long bufferOffset, long length) {
			if(fileOffset + length > Length) {
				Length = fileOffset + length;
			}

			long bytesCopied = 0;
			while(bytesCopied < length) {
				int currentBlock = (int)(fileOffset / BlockSize);
				int currentBlockOffset = (int)(fileOffset - currentBlock * BlockSize);
				long bytesToCopy = Math.Min(BlockSize - currentBlockOffset, Math.Min(length, FileLength - fileOffset));
				if(bytesToCopy == 0) {
					break;
				}

				Array.Copy(buffer, bufferOffset, Blocks[currentBlock], currentBlockOffset, bytesToCopy);

				bytesCopied += bytesToCopy;
				fileOffset += bytesToCopy;
				bufferOffset += bytesToCopy;
				length -= bytesToCopy;
			}
			return bytesCopied;
		}
	}
}
